package com.jgc.locksupport;

import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Administrator on 2022/3/20.
 * LockSupport是JDK中比较底层的类，用来创建锁和其他同步工具类的基本线程阻塞原语。
 * LockSupport很类似于二元信号量(只有1个许可证可供使用)，如果这个许可还没有被占用，当前线程获取许可并继续执行；
 * 如果许可已经被占用，当前线程阻塞，等待获取许可
 *
 * LockSupport.park()和LockSupport.unpark()实现线程的阻塞和唤醒操作的，
 * 当然实现线程间的阻塞和唤醒我们还可以用到对象锁，通过Synchronizer关键字来实现对象同步锁，使用对象的wait()和notify()方法来实现，
 * 但是此方式的实现在性能上会大打折扣而且有些并发控制不当非常容易引发线程间死锁，可以说非常不优雅
 *
 * sum:LockSupport可以用于线程间的阻塞唤醒
 * park()方法，调用native方法阻塞当前线程
 * unpark()方法，唤醒处于阻塞状态的线程Thrread
 */
public class LockSupportDemo2 {
    static Object objectLock = new Object();
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    

    public static void main(String[] args) throws InterruptedException {
    
//        sync_wait_notify();
//        lock_await_signal();
//        locksuport_park_unpark();
        locksuport_park_unpark2();

    }

    /**
     * park给的通行证的上限是1，如果 unpark2次，通行证仍然是1，再park2次，只有第一次park的时候就用完通行证，第二次park时仍然阻塞
     */
    private static void locksuport_park_unpark2() {
        Thread t1 = new Thread(() -> {
            //睡眠几秒钟
            try {
                TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName() + "\t" + "comm in");
            LockSupport.park();
            System.out.println(1);
            LockSupport.park();
            System.out.println(2);
            System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");
        }, "t1");
        t1.start();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "\t" + "---发出通知");
            LockSupport.unpark(t1);
            System.out.println(3);
            LockSupport.unpark(t1);
            System.out.println(4);
        },"t2").start();
    }

    /**
     * unpark可以放在park之后，没有必要的先后顺序，
     * 因为是个通行证的概念，可以先阻塞再给通行证
     */
    private static void locksuport_park_unpark() {
        Thread t1 = new Thread(() -> {
            //睡眠几秒钟
//            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName() + "\t" + "comm in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");
        }, "t1");
        t1.start();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "\t" + "---发出通知");
            LockSupport.unpark(t1);
        },"t2").start();
    }


    /**
     * await signal 的缺点
     * await 和 signal须在lock 代码块下，否则这样报错 IllegalMonitorStateException
     * await 在 signal
     */
    private static void lock_await_signal() {
        new Thread(()->{
//            lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "\t" + "comm in");
                    condition.await();
                    System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
//                    lock.unlock();
                }
        },"t1").start();
        new Thread(()->{
//            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName() + "\t" + "---发出通知");
            }finally {
//                lock.unlock();
            }
        },"t2").start();
    }

    /**wait notify 的缺点
     * wait 和 notify须在synchronized 代码块下，否则这样报错 IllegalMonitorStateException
     * 必须是先wait 在 notify
     * @throws InterruptedException
     */
    private static void sync_wait_notify() throws InterruptedException {
        new Thread(()->{
//            synchronized(objectLock){
                System.out.println(Thread.currentThread().getName() + "\t" + "comm in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");
//            }
        },"t1").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
//            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName() + "\t" + "---发出通知");
//            }
        },"t2").start();
    }
}
